﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Noris.Tools.FrxEditor.Expression
{
    public class Functions : IExpressionFunction
    {
        [FunctionName("min")]
        [FunctionParamCount(FunctionParamCountMode.Minimum, 1)]
        [FunctionParamHint("value[,value2,...]")]
        [FunctionToolTipText("Vrátí nejmenší hodnotu z předaných parametrů")]
        [FunctionListGroupName(Evaluator.LISTGROUP_GENERAL)]
        private EValue FunMin(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            if (parameterList.Count == 0)
                throw new EvalException("Funkce Min() musí mít alespoň jeden parametr.");

            EValue result = EValue.Empty;
            bool first = true;
            foreach (CompiledFormula parameter in parameterList)
            {
                EValue par = parameter.Evaluate(context);
                if (first)
                    result = par;
                else if (par < result)
                    result = par;
            }
            return result;
        }
        [FunctionName("max")]
        [FunctionParamCount(FunctionParamCountMode.Minimum, 1)]
        [FunctionParamHint("value[,value2,...]")]
        [FunctionToolTipText("Vrátí největší hodnotu z předaných parametrů")]
        [FunctionListGroupName(Evaluator.LISTGROUP_GENERAL)]
        private EValue FunMax(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            if (parameterList.Count == 0)
                throw new EvalException("Funkce Max() musí mít alespoň jeden parametr.");

            EValue result = EValue.Empty;
            bool first = true;
            foreach (CompiledFormula parameter in parameterList)
            {
                EValue par = parameter.Evaluate(context);
                if (first)
                    result = par;
                else if (par > result)
                    result = par;
            }
            return result;
        }
        [FunctionName("abs")]
        [FunctionParamCount(1)]
        [FunctionParamHint("value")]
        [FunctionToolTipText("Vrátí absolutní hodnotu z předaného parametru")]
        [FunctionListGroupName(Evaluator.LISTGROUP_NUMERIC)]
        private EValue FunAbs(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce Abs() má mít jeden parametr.", 1);
            EValue par1 = parameterList[0].Evaluate(context);
            switch (par1.Type)
            {
                case EValue.EValueDataType.Int32:
                    return new EValue() { ValueInt32 = Math.Abs(par1.ValueInt32) };
                case EValue.EValueDataType.Int64:
                    return new EValue() { ValueInt64 = Math.Abs(par1.ValueInt64) };
                case EValue.EValueDataType.Decimal:
                    return new EValue() { ValueDecimal = Math.Abs(par1.ValueDecimal) };
            }
            throw new EvalException("Funkci Abs() lze použít pouze na číselných hodnotách. Aktuální hodnota je typu " + par1.Type.ToString());
        }
        [FunctionName("len")]
        [FunctionParamCount(1)]
        [FunctionParamHint("text")]
        [FunctionToolTipText("Vrátí délku předaného textu")]
        [FunctionListGroupName(Evaluator.LISTGROUP_TEXT)]
        private EValue FunLen(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce Len() má mít jeden parametr.", 1);
            EValue par1 = parameterList[0].Evaluate(context);
            return new EValue() { ValueInt32 = par1.ValueString.Length };
        }
        [FunctionName("trim")]
        [FunctionParamCount(1)]
        [FunctionParamHint("text")]
        [FunctionToolTipText("Vrátí předaný text bez úvodních a koncových mezer")]
        [FunctionListGroupName(Evaluator.LISTGROUP_TEXT)]
        private EValue FunTrim(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce Trim() má mít jeden parametr.", 1);
            EValue par1 = parameterList[0].Evaluate(context);
            return new EValue() { ValueString = par1.ValueString.Trim() };
        }
        [FunctionName("substring")]
        [FunctionParamCount(2, 3)]
        [FunctionParamHint("text,start[,length]")]
        [FunctionToolTipText("Vrátí výřez z předaného textu")]
        [FunctionListGroupName(Evaluator.LISTGROUP_TEXT)]
        private EValue FunSubstring(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce Substring() má mít dva nebo tři parametry.", 2, 3);
            EValue par1 = parameterList[0].Evaluate(context);
            string text = par1.ValueString;
            EValue par2 = parameterList[1].Evaluate(context);
            int start = par2.ValueInt32;
            if (parameterList.Count == 2)
            {
                return new EValue() { ValueString = text.Substring(start) };
            }
            else if (parameterList.Count == 3)
            {
                EValue par3 = parameterList[2].Evaluate(context);
                int length = par3.ValueInt32;
                return new EValue() { ValueString = text.Substring(start, length) };
            }
            return EValue.Empty;
        }
        [FunctionName("if")]
        [FunctionParamCount(3)]
        [FunctionParamHint("test, when true, else")]
        [FunctionListGroupName(Evaluator.LISTGROUP_LOGICAL)]
        private EValue FunIf(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce If() má mít tři parametry.", 3);
            EValue parIf = parameterList[0].Evaluate(context);
            if (parIf.ValueBoolean)
                return parameterList[1].Evaluate(context);
            else
                return parameterList[2].Evaluate(context);
        }

        [FunctionName("date")]
        [FunctionParamCount(1)]
        [FunctionParamHint("d")]
        [FunctionToolTipText("Vrátí datum bez času z dodaného data")]
        [FunctionListGroupName(Evaluator.LISTGROUP_DATETIME)]
        private EValue FunDate(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce Date() má mít jeden parametr.", 1);
            EValue par1 = parameterList[0].Evaluate(context);
            return new EValue() { ValueDateTime = par1.ValueDateTime.Date };
        }
        [FunctionName("today")]
        [FunctionParamCount(0)]
        [FunctionParamHint("")]
        [FunctionToolTipText("Vrátí aktuální datum a čas")]
        [FunctionListGroupName(Evaluator.LISTGROUP_DATETIME)]
        private EValue FunToday(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            _CheckParamCount(parameterList, "Funkce Today() nemá mít žádný parametr.", 0);
            return new EValue() { ValueDateTime = DateTime.Now };
        }

        #region IExpressionFunction Members, CheckParams
        /// <summary>
        /// Deklarace, zda název funkce musí odpovídat exaktně (CaseSensitive = true) nebo bez ohledu na velikost písmen (CaseSensitive = false).
        /// </summary>
        bool IExpressionFunction.FunctionNameCaseSensitive
        {
            get { return false; }
        }
        /// <summary>
        /// Ověří skutečný počet parametrů funkce
        /// </summary>
        /// <param name="parameterList"></param>
        /// <param name="text"></param>
        /// <param name="validCount"></param>
        private void _CheckParamCount(List<CompiledFormula> parameterList, string text, params int[] validCount)
        {
            int count = parameterList.Count;
            foreach (int valid in validCount)
            {
                if (count == valid) return;
            }
            throw new EvalException(text + " Aktuální počet parametrů = " + count.ToString());
        }
        #endregion
    }
}
